package com.csap.framework.oauth2.client.rest;

import cn.dev33.satoken.LoginUtil;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.hutool.core.bean.BeanUtil;
import com.csap.framework.extension.CsapException;
import com.csap.framework.extension.result.ResultEnum;
import com.csap.framework.extension.result.ResultParam;
import com.csap.framework.extension.result.ResultUtil;
import com.csap.framework.oauth2.UserInfo;
import com.csap.framework.oauth2.UserInfoStrategyContext;
import com.csap.framework.oauth2.client.Oauth2ClientProperties;
import com.csap.framework.oauth2.client.user.Oauth2UserInfoService;
import com.csap.framework.oauth2.request.LogoutRequest;
import com.csap.framework.oauth2.request.MobileLoginRequest;
import com.csap.framework.oauth2.request.PlatformRequest;
import com.csap.framework.oauth2.request.RegisterRequest;
import com.csap.framework.oauth2.request.RolesRequest;
import com.csap.framework.oauth2.request.RolesResponse;
import com.csap.framework.oauth2.request.TokenRequest;
import com.csap.framework.oauth2.request.UpdateRequest;
import com.csap.framework.oauth2.request.UserPermissionsVo;
import com.csap.framework.oauth2.request.UserRolesRequest;
import com.csap.framework.oauth2.response.RegisterResponse;
import com.csap.framework.oauth2.type.Oauth2ServerType;
import com.csap.framework.util.Assert;
import com.csap.framework.util.CollectionsUtil;
import com.csap.framework.util.StringUtil;
import com.csap.mybatisplus.page.Page;
import com.ejlchina.data.TypeRef;
import com.ejlchina.okhttps.HttpResult;
import com.ejlchina.okhttps.OkHttps;
import com.ejlchina.okhttps.internal.SyncHttpTask;
import lombok.AllArgsConstructor;

import java.util.Map;
import java.util.Set;

import static cn.dev33.satoken.LoginUtil.CLIENT_ID;
import static cn.dev33.satoken.LoginUtil.CLIENT_SECRET;
import static cn.dev33.satoken.LoginUtil.PLATFORM;
import static cn.dev33.satoken.LoginUtil.USER_TYPE;

/**
 * @Author ycf
 * @Date 2021/9/10 1:54 下午
 * @Version 1.0
 */
@AllArgsConstructor
public class Oauth2ClientApiRest implements Oauth2ClientApi {
    /**
     * 客户端配置
     */
    private Oauth2ClientProperties clientProperties;
    /**
     * 用户信息
     */
    private Oauth2UserInfoService<? extends UserInfo> oauth2UserInfoService;
    /**
     * token配置
     */
    private SaTokenConfig saTokenConfig;

    @Override
    public ResultParam<String> queryUserRoleNameByOpenId(UserRolesRequest userRolesRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.queryUserRoleNameByOpenId.getPath())
                .setBodyPara(userRolesRequest))
                .post()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @Override
    public ResultParam<UserInfo> getUserinfo() {
        return ResultUtil.success(oauth2UserInfoService.getUserInfo(UserInfoStrategyContext.userInfo(clientProperties.getUserInfoMod())));
    }

    @Override
    public ResultParam<UserInfo> userInfoId(String loginId) {
        return ResultUtil.success(oauth2UserInfoService.getUserInfo(UserInfoStrategyContext.userInfo(clientProperties.getUserInfoMod(), loginId)));
    }

    /**
     * 模式一，模式三，模式四：Code授权码 获取 Access-Token
     *
     * @param tokenRequest 授权数据
     * @return 返回token
     */
    @Override
    public ResultParam<AccessTokenModel> token(TokenRequest tokenRequest) {
        HttpResult result = header(OkHttps
                        .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.token.getPath())
                        .setBodyPara(tokenRequest
                                .clientId(StringUtil.isEmpty(tokenRequest.getClient_id()) ? clientProperties.getClientId() : tokenRequest.getClient_id())
                                .clientSecret(StringUtil.isEmpty(tokenRequest.getClient_secret()) ? clientProperties.getClientSecret() : tokenRequest.getClient_secret())
                                .loginType(clientProperties.getLoginType()))
                , tokenRequest.getPlatform(), tokenRequest.getLoginType(), tokenRequest.getUserType())
                .post();
        return result.getBody()
                .toBean(new TypeRef<ResultParam<AccessTokenModel>>() {
                })
                .optional()
                .when(i -> !i.getCode().equals(ResultEnum.SUCCESS.getCode()), i -> {
                    throw new CsapException(i.getCode(), i.getMessage());
                })
                .then(i -> setCookie(result))
                .get();
    }

    /**
     * 处理服务端的cookie返回
     *
     * @param result
     */
    private void setCookie(HttpResult result) {
        if (CollectionsUtil.isEmpty(result.getHeaders("Set-Cookie"))) {
            return;
        }
        result.getHeaders("Set-Cookie").forEach(i2 -> {
            SaHolder.getResponse().addHeader("Set-Cookie", i2);
        });
    }

    /**
     * 根据opendId删除用户
     *
     * @param openId openId
     * @return
     */
    @Override
    public ResultParam<Boolean> removeByOpenId(String openId) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.removeByOpenId.getPath())
                .addUrlPara("openId", openId)
                .addUrlPara("clientId", clientProperties.getClientId()))
                .delete()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    /**
     * 普通注册
     *
     * @param registerRequest
     * @return 注册结果
     */
    @Override
    public ResultParam<RegisterResponse> register(RegisterRequest registerRequest) {
        return header(OkHttps
                        .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.register.getPath())
                        .setBodyPara(registerRequest
                                .setClientId(clientProperties.getClientId())
                                .setClientSecret(clientProperties.getClientSecret())
                                .setLoginType(clientProperties.getLoginType())
                                .setPlatform(clientProperties.getPlatform()))
                , registerRequest.getPlatform(), registerRequest.getLoginType(), registerRequest.getUserType())

                .bodyType("json")
                .addHeader("Content-Type", "application/json;charset=utf-8")
                .post()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    /**
     * 一键注册（不存在注册/存在则不注册）
     *
     * @param registerRequest
     * @return 注册结果
     */
    @Override
    public ResultParam<RegisterResponse> onekeyRegister(RegisterRequest registerRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.onekeyRegister.getPath())
                .setBodyPara(registerRequest
                        .setClientId(clientProperties.getClientId())
                        .setClientSecret(clientProperties.getClientSecret())
                        .setLoginType(clientProperties.getLoginType())
                        .setPlatform(clientProperties.getPlatform())), registerRequest.getPlatform(), registerRequest.getLoginType(), registerRequest.getUserType())
                .bodyType("json")
                .addHeader("Content-Type", "application/json;charset=utf-8")
                .post()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    /**
     * 一键登录只支持账号密码
     *
     * @param registerRequest
     * @return
     */
    @Override
    public ResultParam<AccessTokenModel> loginRegister(RegisterRequest registerRequest) {
        HttpResult result = header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.loginRegister.getPath())
                .setBodyPara(registerRequest
                        .setClientId(clientProperties.getClientId())
                        .setClientSecret(clientProperties.getClientSecret())
                        .setLoginType(clientProperties.getLoginType())
                        .setPlatform(clientProperties.getPlatform())), registerRequest.getPlatform(), registerRequest.getLoginType(), registerRequest.getUserType())
                .bodyType("json")
                .post();
        return result.getBody()
                .toBean(new TypeRef<ResultParam<AccessTokenModel>>() {
                })
                .optional()
                .when(i -> !i.getCode().equals(ResultEnum.SUCCESS.getCode()), i -> {
                    throw new CsapException(i.getCode(), i.getMessage());
                })
                .then(i -> setCookie(result))
                .get();
    }


    /**
     * 一键登录只支持手机号
     *
     * @param registerRequest
     * @return token
     */
    @Override
    public ResultParam<AccessTokenModel> loginRegisterMobile(RegisterRequest registerRequest) {
        HttpResult result = header(OkHttps
                        .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.loginRegisterMobile.getPath())
                        .setBodyPara(registerRequest
                                .setClientId(clientProperties.getClientId())
                                .setClientSecret(clientProperties.getClientSecret())
                                .setLoginType(clientProperties.getLoginType())
                                .setPlatform(clientProperties.getPlatform())),
                registerRequest.getPlatform(), registerRequest.getLoginType(), registerRequest.getUserType())
                .bodyType("json")
                .post();
        return result.getBody()
                .toBean(new TypeRef<ResultParam<AccessTokenModel>>() {
                })
                .optional()
                .when(i -> !i.getCode().equals(ResultEnum.SUCCESS.getCode()), i -> {
                    throw new CsapException(i.getCode(), i.getMessage());
                })
                .then(i -> setCookie(result))
                .get();
    }

    /**
     * 手机号码登陆
     *
     * @param mobileRequest 手机号登陆参数
     * @return 返回登录的token
     */
    @Override
    public ResultParam<AccessTokenModel> loginMobile(MobileLoginRequest mobileRequest) {
        HttpResult result = header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.loginMobile.getPath())
                .setBodyPara(mobileRequest
                        .clientId(clientProperties.getClientId())
                        .clientSecret(clientProperties.getClientSecret())
                        .setPlatform(clientProperties.getPlatform())), mobileRequest.getPlatform(), mobileRequest.getLoginType(), mobileRequest.getUserType())
                .bodyType("json")
                .post();
        return result.getBody()
                .toBean(new TypeRef<ResultParam<AccessTokenModel>>() {
                })
                .optional()
                .when(i -> !i.getCode().equals(ResultEnum.SUCCESS.getCode()), i -> {
                    throw new CsapException(i.getCode(), i.getMessage());
                })
                .then(i -> setCookie(result))
                .get();
    }

    /**
     * 根据OpendId修改用户信息
     *
     * @param updateRequest 修改的参数
     * @return 是否修改成功
     */
    @Override
    public ResultParam<Boolean> updateByOpenId(UpdateRequest updateRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.updateByOpenId.getPath())
                .setBodyPara(updateRequest), updateRequest.getPlatform(), updateRequest.getLoginType(), updateRequest.getUserType())
                .bodyType("json")
                .put()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    /**
     * 退出登录
     *
     * @return
     */
    @Override
    public ResultParam<Boolean> loginOut() {
        LoginUtil.getInstance().logout();
        return ResultUtil.success(Boolean.TRUE);
    }

    @Override
    public ResultParam<Boolean> logoutByOpenId(LogoutRequest logoutRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.logoutByOpenId.getPath())
                .addUrlPara("openId", logoutRequest.getOpenId())
                .addUrlPara("clientId", clientProperties.getClientId())
                .addUrlPara("deviceType", logoutRequest.getDeviceType()), logoutRequest.getPlatform(), logoutRequest.getLoginType(), logoutRequest.getUserType())
                .delete()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Boolean> addPlatform(PlatformRequest platformRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.addPlatform.getPath())
                .setBodyPara(platformRequest), platformRequest.getPlatform(), platformRequest.getLoginType(), platformRequest.getUserType())
                .bodyType("json")
                .put()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Boolean> updatePlatform(PlatformRequest platformRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.updatePlatform.getPath())
                .setBodyPara(platformRequest), platformRequest.getPlatform(), platformRequest.getLoginType(), platformRequest.getUserType())
                .bodyType("json")
                .put()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    /**
     * 统一的头部处理
     *
     * @param syncHttpTask httptask
     * @return 返回参数
     */
    private SyncHttpTask header(SyncHttpTask syncHttpTask, String platform, String loginType, String userType) {
        if (StringUtil.isEmpty(platform)) {
            platform = clientProperties.getPlatform();
        }
        if (StringUtil.isEmpty(loginType)) {
            loginType = clientProperties.getLoginType();
        }
        if (StringUtil.isEmpty(userType)) {
            userType = clientProperties.getUserType();
        }
        if (StringUtil.isNotEmpty(platform)) {
            syncHttpTask.addHeader(PLATFORM, platform);
        }
        Assert.notEmpty(loginType, "登录类型未配置");
        Assert.notEmpty(userType, "用户类型未配置");
        return syncHttpTask.addHeader(saTokenConfig.getLoginType(), loginType)
                .addHeader(CLIENT_ID, clientProperties.getClientId())
                .addHeader(CLIENT_SECRET, clientProperties.getClientSecret())
                .addHeader(USER_TYPE, userType);
    }

    /**
     * 处理头部文件
     *
     * @param syncHttpTask 请求对象
     * @return 结果
     */
    private SyncHttpTask header(SyncHttpTask syncHttpTask) {
        return header(syncHttpTask, null, null, null);
    }

    @Override
    public ResultParam<Long> addRole(RolesRequest rolesModel) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.addRole.getPath())
                .setBodyPara(rolesModel))
                .bodyType("json")
                .post()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Boolean> updateRole(RolesRequest rolesModel) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.updateRole.getPath())
                .setBodyPara(rolesModel))
                .bodyType("json")
                .put()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Boolean> remove(Long id) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.removeRole.getPath())
                .addUrlPara("id", id))
                .delete()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Page<RolesResponse>> queryPage(RolesRequest rolesModel) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.queryPageRole.getPath())
                .addUrlPara(BeanUtil.beanToMap(rolesModel)))
                .get()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Boolean> saveOrUpdateUserPermissions(UserPermissionsVo vo) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.saveOrUpdateUserPermissions.getPath())
                .setBodyPara(vo))
                .bodyType("json")
                .put()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Set<Long>> queryUserRolesByOpenId(UserRolesRequest userRolesRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.queryUserRoleByOpenId.getPath())
                .addUrlPara(BeanUtil.beanToMap(userRolesRequest.clientId(clientProperties.getClientId()))))
                .get()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Map<Long, RolesResponse>> findMapByIds(String ids) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.rolesFindMapByIds.getPath())
                .addUrlPara("ids", ids))
                .get()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }

    @Override
    public ResultParam<Boolean> addUserRoles(UserRolesRequest userRolesRequest) {
        return header(OkHttps
                .sync(clientProperties.getOauth2ServerUrl() + Oauth2ServerType.addUserRole.getPath())
                .setBodyPara(userRolesRequest.clientId(clientProperties.getClientId())))
                .bodyType("json")
                .post()
                .getBody()
                .toBean(new TypeRef<>() {
                });
    }
}
